package com.ganzalang.gmall.concurrentart.jvm;

/**
 * @author Ganzalang
 * @description
 * @Date 2022/4/24 21:58
 */
public class DoubleCheckLockSingleton {

    private static DoubleCheckLockSingleton instance = null;

    public DoubleCheckLockSingleton() {

    }


    public static DoubleCheckLockSingleton getInstance() {
        if (instance == null) { // 2. 此时来了一个线程2，因为instance字段目前已经是非null，所以直接return了出去一个未完全初始化的instance对象。
            /**
             * 字节码
             * 10 monitorenter
             * 11 getstatic #2 <com/ganzalang/gmall/concurrentart/jvm/DoubleCheckLockSingleton.instance : Lcom/ganzalang/gmall/concurrentart/jvm/DoubleCheckLockSingleton;>
             * 14 ifnonnull 27 (+13)
             * 17 new #3 <com/ganzalang/gmall/concurrentart/jvm/DoubleCheckLockSingleton>
             * 20 dup
             * 21 invokespecial #4 <com/ganzalang/gmall/concurrentart/jvm/DoubleCheckLockSingleton.<init> : ()V>
             * 24 putstatic #2 <com/ganzalang/gmall/concurrentart/jvm/DoubleCheckLockSingleton.instance : Lcom/ganzalang/gmall/concurrentart/jvm/DoubleCheckLockSingleton;>
             * 27 aload_0
             * 28 monitorexit
             */
            synchronized (DoubleCheckLockSingleton.class) {
                if (instance == null) {
                    /**
                     * 1. 线程1执行new DoubleCheckLockSingleton，而该代码是有若干条指令组成的。
                     * 1）第一条指令是上面字节码中的序号17的new指令，表示new一个对象，划分一个内存区域给一个对象，但是该对象的值还是0值，还没有完全初始化
                     * 2）第二条指令是上面字节码中的序号20的dup指令，表示复制栈顶的内容，此处不需要关注
                     * 3）第三条指令是上面字节码中的序号21的invokespecial指令，表示对一个对象执行init方法，做初始化操作，即初始化对象的值
                     * 4）第四条指令是上面字节码中的序号24的putstatic指令，表示将new指令出来的对象赋值给DoubleCheckLockSingleton类的instance字段
                     * 在同一个线程内，调整putstatic和invokespecial指令对最终结果没有影响，因此虚拟机有可能会对这两个指令重排序。
                     * 当先执行putstatic指令再执行invokespecial指令，即将一个未完全初始化的对象复制给了instance字段，再执行初始化。
                     * 那么当执行到第一个if的时候，见第一个if的注释
                     */
                    instance = new DoubleCheckLockSingleton();
                }
            }
        }
        return instance;
    }
}
